# encoding: utf-8
""" 
@version: v1.0 
@author: autumner 
@license: Apache Licence  
@contact: 18322313385@163.com 
@site:  https://gitee.com/autumner/pythoncookbook
@software: PyCharm 
@file: meta_ast_parse_namelower.py 
@time: 2019/7/9 下午2:32
"""
'''
通过重新解析函数体源码、重写 AST 并重新创建函数代码
对象来将全局访问变量降为函数体作用范围
'''
import ast
import inspect

"""
Node visitor that lowers globally accessed name into 
the function body as local variables.
"""
class NameLower(ast.NodeVisitor):
    def __init__(self, lowered_names):
        self.lowered_names = lowered_names

    def visit_FunctionDef(self, node):
        code = '__globals = globals()\n'
        code += '\n'.join("{0} = __globals['{0}']".format(name) for name in self.lowered_names)
        code_ast = ast.parse(code, mode='exec')
        node.body[:0] = code_ast.body
        self.func = node

def lower_names(*namelist):
    def lower(func):
        srclines = inspect.getsource(func).splitlines()
        for n, line in enumerate(srclines):
            if '@lower_names' in line:
                break

        src = '\n'.join(srclines[n+1:])
        if src.startswith(('  ', '\t')):
            src = 'if 1:\n' + src
        top = ast.parse(src, mode='exec')

        c1 = NameLower(namelist)
        c1.visit(top)

        temp = {}
        exec(compile(top, '', 'exec'), temp, temp)

        func.__code__ = temp[func.__name__].__code__
        return func
    return lower

    INCR =1
    @lower_names('INCR')
    def countdown(n):
        while n > 0:
            n -= INCR
